﻿package kit4j.ui.cs.swing.component;

import java.awt.Font;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JList;

import kit4j.ui.cs.FontStyle;
import kit4j.ui.cs.exception.ListItemNotFoundException;
import kit4j.ui.cs.exception.ListNoSelectionException;

/**
 * 显示字体风格列表并且允许用户选择一个或多个项的组件。使用方法类似JList。<br />
 * 以下代码创建一个显示字体风格的列表并选择该列表默认字体风格值的第一项，然后打印列表当前选择的字体风格信息：
 * 
 * <pre>
 * <code>
 * 	JFontStyleList list = new JFontStyleList();
 * 	list.setSelectedFontStyle(JFontStyleList.DEFAULT_FONT_STYLES[0]);
 * 	System.out.println(fontList.getSelectedFontStyle());
 * </code>
 * </pre>
 * 
 * @author MiniKnife
 */
public class JFontStyleList extends JList {

	private static final long serialVersionUID = 1L;

	/**
	 * 默认字体风格。
	 */
	public static final FontStyle[] DEFAULT_FONT_STYLES = { new FontStyle("常规", Font.PLAIN),
			new FontStyle("粗体", Font.BOLD), new FontStyle("斜体", Font.ITALIC),
			new FontStyle("粗体 斜体", Font.BOLD + Font.ITALIC) };

	/**
	 * 字体风格值-字体风格对应集。列表通过它使用字体风格值定位到列表中的某个字体风格。
	 */
	private Map<Integer, FontStyle> fontStyleValueMap;

	public JFontStyleList() {
		this(null);
	}

	public JFontStyleList(FontStyle[] fontSizes) {
		super();

		setCellRenderer(new JFontStyleListCellRenderer());

		fontStyleValueMap = new HashMap<Integer, FontStyle>();

		if (fontSizes != null) {
			setListData(fontSizes);
		} else {
			setListData(DEFAULT_FONT_STYLES);
		}
	}

	/**
	 * 返回最小的选择单元索引的字体风格；只选择了列表中单个项时，返回所选值。选择了多项时，返回最小的选择索引的值。
	 * 
	 * @return 当所选的第一个字体风格
	 * @throws ListNoSelectionException
	 *             如果列表什么也没有选择，抛出此异常。
	 */
	public FontStyle getSelectedFontStyle() throws ListNoSelectionException {
		FontStyle font = (FontStyle) super.getSelectedValue();

		if (font == null) {
			throw new ListNoSelectionException();
		}

		return font;
	}

	/**
	 * 从列表中选择指定的字体风格。
	 * 
	 * @param style
	 *            要选择的字体风格
	 * @param shouldScroll
	 *            如果所选字体风格存在，但列表需要滚动才能显示，则为 true；否则为 false
	 * @throws ListItemNotFoundException
	 *             如果列表中不存在此字体风格，则抛出此异常。
	 */
	public void setSelectedFontStyle(FontStyle style, boolean shouldScroll) throws ListItemNotFoundException {
		setSelectedFontStyleByValue(style.getValue(), shouldScroll);
	}

	/**
	 * 从列表中选择具有指定字体风格值的字体风格。
	 * 
	 * @param value
	 *            要选择的字体风格的值
	 * @param shouldScroll
	 *            如果所选字体风格存在，但列表需要滚动才能显示，则为 true；否则为 false
	 * @throws ListItemNotFoundException
	 *             如果列表中不存在此字体风格，则抛出此异常。
	 */
	public void setSelectedFontStyleByValue(int value, boolean shouldScroll) throws ListItemNotFoundException {
		FontStyle f = fontStyleValueMap.get(value);

		if (f == null) {
			throw new ListItemNotFoundException();
		}

		super.setSelectedValue(f, shouldScroll);
	}

	/**
	 * 返回所有选择字体风格的数组，根据其列表中的索引顺序按升序排序。
	 * 
	 * @return 所选字体风格
	 * @throws ListNoSelectionException
	 *             如果列表什么也没有选择，抛出此异常。
	 */
	public FontStyle[] getSelectedFontStyles() throws ListNoSelectionException {
		FontStyle[] fonts = (FontStyle[]) super.getSelectedValues();

		if (fonts.length == 0) {
			throw new ListNoSelectionException();
		}

		return fonts;
	}

	@Override
	/**
	 * 从列表中选择指定的字体风格。如果列表中不存在此字体风格，则什么也不做，建议使用替代方法
	 * JFontStyleList#setSelectedFontStyle(FontStyle, boolean)。
	 * @param font 要选择的字体风格
	 * @param shouldScroll 如果所选字体风格存在，但列表需要滚动才能显示，则为 true；否则为 false
	 * @throws IllegalArgumentException 如果参数对象为null或参数不是FontStyle类型，抛出此异常。
	 */
	public void setSelectedValue(Object font, boolean shouldScroll) throws IllegalArgumentException {
		if (font == null) {
			throw new IllegalArgumentException("Argument should't be null!");
		}

		if (!(font instanceof FontStyle)) {
			throw new IllegalArgumentException("Argument must be a FontStyle");
		}

		try {
			setSelectedFontStyle((FontStyle) font, shouldScroll);
		} catch (ListItemNotFoundException e) {
			return;
		}
	}

	@Override
	/**
	 * 根据一个字体风格对象数组构造只读 ListModel，然后对此模型调用 setModel。 
	 * @throws IllegalArgumentException 如果参数对象为null或参数不是FontStyle类型数组，抛出此异常。
	 */
	public void setListData(Object[] fonts) throws IllegalArgumentException {
		if (fonts == null) {
			throw new IllegalArgumentException("Argument should't be null!");
		}

		if (!(fonts instanceof FontStyle[])) {
			throw new IllegalArgumentException("Argument must be a FontStyle list");
		}

		// 初始化名称-字体风格对应集
		fontStyleValueMap.clear();
		for (FontStyle fs : (FontStyle[]) fonts) {
			fontStyleValueMap.put(fs.getValue(), fs);
		}

		super.setListData(fonts);
	}

	/**
	 * 返回字体风格列表中的字体风格对象数组。
	 * 
	 * @return 字体风格对象数组
	 */
	public FontStyle[] getListFontStyles() {
		return fontStyleValueMap.values().toArray(new FontStyle[0]);
	}

	/**
	 * 根据一个字体风格对象数组构造只读 ListModel，然后对此模型调用 setModel。
	 * 
	 * @throws IllegalArgumentException
	 *             如果参数对象为null或参数不是FontStyle类型数组，抛出此异常。
	 */
	public void setListFontStyles(FontStyle[] data) throws IllegalArgumentException {
		this.setListData(data);
	}
}
